/*
* @Author: ystlong
* @Date:   2018-06-11 19:13:04
* @Last Modified by:   ystlong
* @Last Modified time: 2018-06-11 23:04:08

*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "bfd.h"
#include "dis-asm.h"

// // unsigned int, char*, size_t, int, int -> insn_t *
// // Disassemble one instruction from the given buf
// insn_t * disassemble_one(unsigned long long vma, char * rawbuf, size_t buflen, int arch, int bits, int endian){
//     insn_t * curri = NULL;
//     bfd_byte* buf = NULL;
//     size_t pos = 0;

//     dis = (struct disassemble_info*) calloc(1, sizeof(disassemble_info));
    
//     if(!dis){
//         return NULL;
//     }
   
//     init_disassemble_info (dis, stdout, my_fprintf);
//     buf = (bfd_byte*) rawbuf;

//     disas_options = NULL;
//     if(init_dis_env(arch, bits, endian)){
//         return NULL;
//     }
 
//     dis->buffer_vma = vma;
//     dis->buffer = buf;
//     dis->buffer_length = buflen;
//     dis->print_address_func = override_print_address;
//     dis->disassembler_options = disas_options;

//     pos = vma;

  
//     curri = (insn_t *) malloc(sizeof(insn_t));
//     if(!curri){
//         perror("malloc");
//         return NULL;
//     }

//     curri->vma = pos;
//     int size = disas((bfd_vma) pos, dis);
//     curri->instr_size = size;
    
//     if(size < 1){
//         return NULL;
//     }

//     char * opcodes = (char *) malloc(size);
//     if(!opcodes){
//         perror("malloc");
//         return NULL;
//     }

//     if(arch == ARCH_x86){
//         copy_bytes_x86(opcodes, rawbuf + (pos - vma), size);
//     }else{
//         copy_bytes(opcodes, rawbuf + (pos - vma), size);
//     }
//     curri->opcodes = opcodes;

//     size_t istrlen = strlen(curr_insn_str);
//     char * decoded_instrs = (char *) malloc(istrlen + 1);
//     if(!decoded_instrs){
//         perror("malloc");
//         return NULL;
//     }
   
//     memcpy(decoded_instrs, curr_insn_str, istrlen + 1);
//     curri->decoded_instrs = decoded_instrs;

//     memset(curr_insn_str, 0, sizeof(curr_insn_str));
//     currptr = curr_insn_str;
//     pos += size;

//     free(dis);
//     return curri;
// }

// bfd_vma, struct disassemble_info * -> void
// Formatter for address in memory referencing instructions
void override_print_address(bfd_vma addr, struct disassemble_info *info){
	// printf("%")
	if ((long)addr >= 0)
		printf("#{pc}+%ld", addr);
	else
		printf("#{pc}%ld", addr);
	// printf("%p", (void *)addr);
    // sprintf(currptr, "%p", (void *) addr);
}

// char * disas_options;


// int init_dis_env(int arch, int bits, int endian){
   
//     switch(arch){
//     	case ARCH_aarch64:

//     	break;
//         // case ARCH_arm:
//         //     if(endian) disas = print_insn_big_arm;
//         //     else disas = print_insn_little_arm;
//         //     if(bits == 16) disas_options = "force-thumb";
//         //     else if (bits == 64) disas = print_insn_aarch64;
//         //     else disas_options = "no-force-thumb";
//         //     break;
//         // case ARCH_mips: 
//         //     if(endian) disas = print_insn_big_mips;
//         //     else disas = print_insn_little_mips; 
//         //     if(bits == 64) dis->mach = bfd_mach_mipsisa64;
//         //     break;

//         // case ARCH_powerpc: 
//         //     if(endian) disas = print_insn_big_powerpc;
//         //     else disas = print_insn_little_powerpc;
//         //     if (bits == 64) disas_options = "64";
//         //     dis->arch = bfd_arch_powerpc;       // ppc cares about this
//         //     disassemble_init_for_target(dis);   // otherwise segfault
//         //     break;

//         // case ARCH_x86:
//         //     if (bits == 16) dis->mach = bfd_mach_i386_i8086;
//         //     else if (bits == 64) dis->mach = !(bfd_mach_i386_i8086 | bfd_mach_i386_i386);
//         //     else dis->mach = bfd_mach_i386_i386;
//         //     disas = print_insn_i386_intel;
//         //     break;

//         default:
//             fprintf(stderr, "libxdisasm: Invalid architecture\n");
//             return -1;
//     }

//     return 0;

// }

// int
// print_insn_aarch64 (bfd_vma pc,
// 		    struct disassemble_info *info)
int main() {

	long pc;
	struct disassemble_info dis_info;
	struct disassemble_info *dis = &dis_info;
	bfd_byte buf[120];
	long *vma = &pc;
	int buflen = 120;
	char *disas_options = "";
	memset(&dis_info, 0, sizeof(dis_info));

    init_disassemble_info (&dis_info, stdout, (fprintf_ftype) fprintf);

    dis->buffer_vma = pc;
    dis->buffer = buf;
    dis->buffer_length = buflen;
    dis->print_address_func = override_print_address;
    // dis->disassembler_options = disas_options;
    dis->fprintf_func = fprintf;
    // dis->stream = stdout;
    dis->arch = bfd_arch_aarch64;
    dis->mach = bfd_mach_aarch64;
    // dis->endian = BFD_ENDIAN_LITTLE;
    // dis->endian_code = BFD_ENDIAN_LITTLE;
    // dis->insn_sets = ???
    // dis->section = ???
    dis->octets_per_byte = 4;

  /* Function used to get bytes to disassemble.  MEMADDR is the
     address of the stuff to be disassembled, MYADDR is the address to
     put the bytes in, and LENGTH is the number of bytes to read.
     INFO is a pointer to this struct.
     Returns an errno value or 0 for success.  */
  // int (*read_memory_func)
  //   (bfd_vma memaddr, bfd_byte *myaddr, unsigned int length,
  //    struct disassemble_info *dinfo);



// 0000000000000000 <_start>:
//    0:   aa0203e1        mov     x1, x2


	pc = 0;
	unsigned int mov_inst[] = {
		0xaa0203e1,
		0x14000000,
		0x17ffffff,
		0x94000002
	};
	printf("len: %d\n", sizeof(mov_inst));
	// dis->disassembler_needs_relocs = 1;
	for(int i=0; i < sizeof(mov_inst); i++) {
		memcpy(dis->buffer, &mov_inst[i], sizeof(mov_inst[i]));
		printf("aaa: %x\n", *(unsigned int*)dis->buffer);
		print_insn_aarch64(pc, &dis_info);
		printf("\n");
	}
    return 0;
}